#include <string>

#include "DataStore.h"

DataStore::DataStore()
{
    populate();
}

DataStore::~DataStore()
{
}

void DataStore::populate()
{
    decode();
    readAccounts();
    readBookings();
    readHolidays();
    readMail();
}

void DataStore::commit()
{
    writeAccounts();
    writeBookings();
    writeHolidays();
    writeMail();
    encode();
}

int DataStore::findBookRef(int input)
{
    int size = bookings.size();
    for (int i = 0; i < size; i++)
        if (input == bookings[i].ref)
            return i + 1;
    return 0;
}

int DataStore::findUserID(int input)
{
    int size = accounts.size();
    for (int i = 0; i < size; i++)
        if (input == accounts[i].ID)
            return i + 1;
    return 0;
}

int DataStore::findUserName(string input)
{
    int size = accounts.size();
    for (int i = 0; i < size; i++)
        if (input == accounts[i].user)
            return i + 1;
    return 0;
}

int DataStore::findHolCode(int input)
{
    int size = holidays.size();
    for (int i = 0; i < size; i++)
        if (input == holidays[i].code)
            return i + 1;
    return 0;
}

deque<bookStruct> DataStore::listClientBookings(int id)
{
    deque<bookStruct> temp;
    int size = bookings.size();
    for (int i = 0; i < size; i++)
        if (id == bookings[i].clientID)
            temp.push_back(bookings[i]);
    return temp;
}

deque<bookStruct> DataStore::listHolidayBookings(int code)
{
    deque<bookStruct>temp;
    int size = bookings.size();
    for (int i = 0; i < size; i++)
        if (code == bookings[i].code)
            temp.push_front(bookings[i]);
    return temp;
}

bool DataStore::hasColon(string input)
{
    int size = input.size();
    for (int i = 0; i < size; i++)
        if (input[i] == ':')
            return true;
    return false;
}

string DataStore::accountString(accountType input)
{
    string temp;
    switch ((int) input)
    {
    case 0: temp = "Client";
        break;
    case 1: temp = "Booking Staff";
        break;
    case 2: temp = "Finance Staff";
        break;
    case 3: temp = "Holiday Manager";
        break;
    case 4: temp = "General Manager";
        break;
    }
    return temp;
}

string DataStore::holidayString(holidayType input)
{
    string temp;
    switch ((int) input)
    {
    case 0: temp = "The Small Adventure";
        break;
    case 1: temp = "The Triers Adventure";
        break;
    case 2: temp = "The Great Adventure";
        break;
    case 3: temp = "The Really Big Adventure";
        break;
    }
    return temp;
}

string DataStore::bookingString(bookingStatus input)
{
    string temp;
    switch ((int) input)
    {
    case 0: temp = "New Booking";
        break;
    case 1: temp = "Edited, Awaiting Recheck";
        break;
    case 2: temp = "Incomplete Booking";
        break;
    case 3: temp = "Invalid Booking";
        break;
    case 4: temp = "Checked, Awaiting Manager";
        break;
    case 5: temp = "On Waiting List";
        break;
    case 6: temp = "Payment Required";
        break;
    case 7: temp = "Booking Confirmed";
        break;
    case 8: temp = "Cancelled by Customer";
        break;
    }
    return temp;
}

void DataStore::readAccounts()
{
    file.open("accounts.txt", ios::in);
    int count = countLines();
    accounts.clear();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        accountStruct temp;
        temp.ID = 0;

        getline(file, temp.user, ':');
        getline(file, temp.pass, ':');

        int input;
        file >> input;
        temp.type = (accountType) input;

        if (input == 0 || input == 3)
        {
            file.ignore();

            file >> input;
            temp.ID = input;
        }
        file.ignore(INT_MAX, '\n');
        accounts.push_back(temp);
    }
    file.close();
}

void DataStore::readBookings()
{
    file.open("bookings.txt", ios::in);
    int count = countLines();
    bookings.clear();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        bookStruct temp;

        int input;
        file >> input;
        temp.ref = input;
        file.ignore();

        getline(file, temp.name, ':');
        getline(file, temp.address, ':');

        file >> input;
        temp.code = input;
        file.ignore();

        getline(file, temp.destination, ':');

        file >> input;
        temp.type = (holidayType) input;
        file.ignore();

        getline(file, temp.startDate1, ':');
        getline(file, temp.endDate1, ':');
        getline(file, temp.startDate2, ':');
        getline(file, temp.endDate2, ':');

        getline(file, temp.special, ':');
        getline(file, temp.bookDate, ':');

        file >> input;
        temp.status = (bookingStatus) input;
        file.ignore();

        file >> input;
        temp.clientID = input;

        file.ignore(INT_MAX, '\n');

        bookings.push_back(temp);
    }
    file.close();
}

void DataStore::readHolidays()
{
    file.open("holidays.txt", ios::in);
    int count = countLines();
    holidays.clear();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        holStruct temp;

        int input;
        file >> input;
        temp.code = input;
        file.ignore();

        getline(file, temp.destination, ':');

        file >> input;
        temp.type = (holidayType) input;
        file.ignore();

        file >> input;
        temp.managerID = input;

        file.ignore(INT_MAX, '\n');
        holidays.push_back(temp);
    }
    file.close();
}

void DataStore::readMail()
{
    file.open("mailbox.txt", ios::in);
    int count = countLines();
    mails.clear();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        mail temp;

        int input;
        file >> input;
        temp.read = input;
        file.ignore();

        getline(file, temp.user, ':');

        getline(file, temp.contents, ':');

        file.ignore(INT_MAX, '\n');

        mails.push_back(temp);
    }
    file.close();
}

int DataStore::countLines()
{
    int lines = count(istreambuf_iterator<char>(file),
                      istreambuf_iterator<char>(), '\n');
    file.seekg(0, ios::beg);
    return lines;
}

void DataStore::writeAccounts()
{
    file.open("accounts.txt", ios::out);
    int count = accounts.size();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        stringstream output;
        output << accounts[i].user;
        output << ":" << accounts[i].pass;
        output << ":" << (int) accounts[i].type;
        if (accounts[i].type == Client || accounts[i].type == HolidayM)
        {
            output << ":" << accounts[i].ID;
        }
        output << "\r\n";
        string writedata = output.str();
        file << writedata;
    }
    file.close();
}

void DataStore::writeBookings()
{
    file.open("bookings.txt", ios::out);
    int count = bookings.size();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        stringstream output;
        output << bookings[i].ref;
        output << ":" << bookings[i].name;
        output << ":" << bookings[i].address;
        output << ":" << bookings[i].code;
        output << ":" << bookings[i].destination;
        output << ":" << (int) bookings[i].type;
        output << ":" << bookings[i].startDate1;
        output << ":" << bookings[i].endDate1;
        output << ":" << bookings[i].startDate2;
        output << ":" << bookings[i].endDate2;
        output << ":" << bookings[i].special;
        output << ":" << bookings[i].bookDate;
        output << ":" << (int) bookings[i].status;
        output << ":" << bookings[i].clientID;
        output << "\r\n";
        string writedata = output.str();
        file << writedata;
    }
    file.close();
}

void DataStore::writeHolidays()
{
    file.open("holidays.txt", ios::out);
    int count = holidays.size();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        stringstream output;
        output << holidays[i].code;
        output << ":" << holidays[i].destination;
        output << ":" << (int) holidays[i].type;
        output << ":" << holidays[i].managerID;
        output << "\r\n";
        string writedata = output.str();
        file << writedata;
    }
    file.close();
}

void DataStore::writeMail()
{
    file.open("mailbox.txt", ios::out);
    int count = mails.size();
    for (int i = 0; i < count; i++)
    {
        file.clear();
        stringstream output;
        output << mails[i].read;
        output << ":" << mails[i].user;
        output << ":" << mails[i].contents << ":";
        output << "\r\n";
        string writedata = output.str();
        file << writedata;
    }
    file.close();
}

void DataStore::removeHolidays(const int id)
{
    holidays.erase(holidays.begin() + id);
}

void DataStore::removeUser(const int id)
{
    accounts.erase(accounts.begin() + id);
}

string DataStore::encryptDecrypt(string toEncrypt)
{
    char key[3] = {'K', 'C', 'Q'}; //Any chars will work, in an array of any size
    string output = toEncrypt;

    for (int i = 0; i < toEncrypt.size(); i++)
        output[i] = toEncrypt[i] ^ key[i % (sizeof (key) / sizeof (char))];

    return output;
}

void DataStore::encode()
{
    string unencoded, encoded;
    stringstream reader1, reader2, reader3, reader4;

    // Encode accounts.txt and erase it
    file.open("accounts.txt", ios::in);
    unencoded.clear();
    reader1 << file.rdbuf();
    unencoded = reader1.str();
    file.close();

    file.open("encodedA.txt", ios::out);
    encoded.clear();
    encoded = encryptDecrypt(unencoded);
    file << encoded;
    file.close();
    file.open("accounts.txt", ios::trunc | ios::out);
    file.close();

    // Encode bookings.txt and erase it
    file.open("bookings.txt", ios::in);
    unencoded.clear();
    reader2 << file.rdbuf();
    unencoded = reader2.str();
    file.close();

    file.open("encodedB.txt", ios::out);
    encoded.clear();
    encoded = encryptDecrypt(unencoded);
    file << encoded;
    file.close();
    file.open("bookings.txt", ios::trunc | ios::out);
    file.close();

    // Encode holidays.txt and erase it
    file.open("holidays.txt", ios::in);
    unencoded.clear();
    reader3 << file.rdbuf();
    unencoded = reader3.str();
    file.close();

    file.open("encodedH.txt", ios::out);
    encoded.clear();
    encoded = encryptDecrypt(unencoded);
    file << encoded;
    file.close();
    file.open("holidays.txt", ios::trunc | ios::out);
    file.close();

    // Encode mailbox.txt and erase it
    file.open("mailbox.txt", ios::in);
    unencoded.clear();
    reader4 << file.rdbuf();
    unencoded = reader4.str();
    file.close();

    file.open("encodedM.txt", ios::out);
    encoded.clear();
    encoded = encryptDecrypt(unencoded);
    file << encoded;
    file.close();
    file.open("mailbox.txt", ios::trunc | ios::out);
    file.close();
}

void DataStore::decode()
{
    string unencoded, encoded;
    stringstream reader1, reader2, reader3, reader4;

    // Decodes to accounts.txt
    file.open("encodedA.txt", ios::in);
    encoded.clear();
    reader1 << file.rdbuf();
    encoded = reader1.str();
    file.close();

    file.open("accounts.txt", ios::out);
    unencoded.clear();
    unencoded = encryptDecrypt(encoded);
    file << unencoded;
    file.close();

    // Decodes to bookings.txt
    file.open("encodedB.txt", ios::in);
    encoded.clear();
    reader2 << file.rdbuf();
    encoded = reader2.str();
    file.close();

    file.open("bookings.txt", ios::out);
    unencoded.clear();
    unencoded = encryptDecrypt(encoded);
    file << unencoded;
    file.close();

    // Decodes to holidays.txt
    file.open("encodedH.txt", ios::in);
    encoded.clear();
    reader3 << file.rdbuf();
    encoded = reader3.str();
    file.close();

    file.open("holidays.txt", ios::out);
    unencoded.clear();
    unencoded = encryptDecrypt(encoded);
    file << unencoded;
    file.close();

    // Decodes to mailbox.txt
    file.open("encodedM.txt", ios::in);
    encoded.clear();
    reader4 << file.rdbuf();
    encoded = reader4.str();
    file.close();

    file.open("mailbox.txt", ios::out);
    unencoded.clear();
    unencoded = encryptDecrypt(encoded);
    file << unencoded;
    file.close();
}